﻿using System;
using System.Configuration;
using System.IO;
using System.Reflection;
using Neovolve.Extensibility.VisualStudio.ReflectorLink.Properties;

namespace Neovolve.Extensibility.VisualStudio.ReflectorLink
{
    internal static class ReflectorResolver
    {
        private const String ReflectorPathKey = "ReflectorPath";

        /// <summary>
        /// Gets the reflector path.
        /// </summary>
        /// <returns></returns>
        public static String GetReflectorPath()
        {
            ResolvePath();

            return ReflectorPath;
        }

        /// <summary>
        /// Sets the reflector path.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="saveToConfiguration">if set to <c>true</c> to save the value to configuration.</param>
        public static void SetReflectorPath(String path, Boolean saveToConfiguration)
        {
            ReflectorPath = path;

            // Exit if we are not saving the value to configuration
            // This would be called when the value has just been read from configuration and we don't want to save it back again, but just assign the value to the property.
            if (saveToConfiguration == false)
            {
                return;
            }

            // Load the configuration
            String assemblyPath = Assembly.GetExecutingAssembly().Location;
            Configuration config = ConfigurationManager.OpenExeConfiguration(assemblyPath);

            // Update and save the configuration
            if (config.AppSettings.Settings[ReflectorPathKey] != null)
            {
                config.AppSettings.Settings[ReflectorPathKey].Value = path;
            }
            else
            {
                config.AppSettings.Settings.Add(new KeyValueConfigurationElement(ReflectorPathKey, path));
            }

            config.Save();
        }

        /// <summary>
        /// Checks the path.
        /// </summary>
        /// <param name="directoryPath">The directory path.</param>
        /// <returns>True if Reflector is found, otherwise false.</returns>
        private static Boolean CheckPath(String directoryPath)
        {
            String resolvePath = Path.Combine(directoryPath, Resources.ReflectorAssemblyName);

            // Check if the file is found
            if (File.Exists(resolvePath))
            {
                String fullPath = Path.GetFullPath(resolvePath);

                SetReflectorPath(fullPath, false);

                return true;
            }

            return false;
        }

        /// <summary>
        /// Resolves the path.
        /// </summary>
        private static void ResolvePath()
        {
            // Check if the path has already been resolved
            if (String.IsNullOrEmpty(ReflectorPath) == false)
            {
                return;
            }

            // Check if the configuration contains the known location
            String assemblyPath = Assembly.GetExecutingAssembly().Location;
            Configuration config = ConfigurationManager.OpenExeConfiguration(assemblyPath);

            // Check if the path is stored in configuration
            if (config.AppSettings.Settings[ReflectorPathKey] != null)
            {
                String path = config.AppSettings.Settings[ReflectorPathKey].Value;

                // Check if the configuration correctly identifies the application
                if (String.IsNullOrEmpty(path) == false
                    && File.Exists(path))
                {
                    SetReflectorPath(path, false);

                    return;
                }
            }

            const String PathEnvironmentVariableName = "Path";
            String environmentValue = Environment.GetEnvironmentVariable(PathEnvironmentVariableName);

            if (String.IsNullOrEmpty(environmentValue) == false)
            {
                String[] environmentPaths = environmentValue.Split(Path.PathSeparator);

                foreach (String environmentPath in environmentPaths)
                {
                    // Check if the path was found
                    if (SearchDirectoryCombinations(environmentPath))
                    {
                        return;
                    }
                }
            }

            String programFilesPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);

            if (SearchDirectoryCombinations(programFilesPath))
            {
                return;
            }

            DriveInfo[] drives = DriveInfo.GetDrives();
            String programFilesDriveName = Path.GetPathRoot(programFilesPath);

            for (Int32 index = 0; index < drives.Length; index++)
            {
                DriveInfo drive = drives[index];

                if ((drive.DriveType == DriveType.Fixed)
                    && (programFilesDriveName != drive.Name))
                {
                    // Search the paths on this drive
                    String driveSearchPath = drive.Name + programFilesPath.Substring(drive.Name.Length);

                    if (SearchDirectoryCombinations(driveSearchPath))
                    {
                        return;
                    }
                }
            }
        }

        /// <summary>
        /// Searches the directory combinations.
        /// </summary>
        /// <param name="basePath">The base path.</param>
        /// <returns>True if Reflector is found, otherwise false.</returns>
        private static Boolean SearchDirectoryCombinations(String basePath)
        {
            // Attempt to manually resolve the path

            // Check if the assembly exists in the base path
            if (CheckPath(basePath))
            {
                return true;
            }

            // Check if the assembly exists in basePath/Reflector
            if (CheckPath(Path.Combine(basePath, Resources.Reflector)))
            {
                return true;
            }

            // Check if the assembly exists in basePath/Author
            String authorPath = Path.Combine(basePath, Resources.ReflectorAuthor);

            if (CheckPath(authorPath))
            {
                return true;
            }

            // Check if the assembly exists in basePath/Author/Reflector
            return CheckPath(Path.Combine(authorPath, Resources.Reflector));
        }

        /// <summary>
        /// Gets a value indicating whether this instance can resolve.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance can resolve; otherwise, <c>false</c>.
        /// </value>
        public static Boolean CanResolve
        {
            get
            {
                ResolvePath();

                return (String.IsNullOrEmpty(ReflectorPath) == false && File.Exists(ReflectorPath));
            }
        }

        /// <summary>
        /// Gets or sets the reflector path.
        /// </summary>
        /// <value>The reflector path.</value>
        private static String ReflectorPath
        {
            get;
            set;
        }
    }
}